package vg.modules.attribute_panel;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang.mutable.MutableBoolean;
import vg.modules.attribute_panel.components.AttributeTableCellEditor;
import vg.modules.attribute_panel.components.AttributeTableCellRenderer;
import vg.modules.attribute_panel.components.AttributeTableModel;
import vg.modules.attribute_panel.components.data.AttributeRow;
import vg.modules.attribute_panel.components.data.AttributeShower;
import vg.modules.attribute_panel.components.data.AttributeValue;
import vg.interfaces.ll_services.data_base_service.data.graph.Attribute;
import vg.services.graph_view_manager.interfaces.graph_comparison.AGraphComparisonView;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.graph_view_manager.interfaces.graph_view.GraphObserverAdapter;
import vg.services.graph_view_manager.interfaces.graph_view.IGraphObserver;
import vg.services.graph_view_manager.interfaces.graph_view.IGraphRenderer;
import vg.services.graph_view_manager.interfaces.tab.ATab;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.GetSelectedElementAttributes;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.GraphObserverAction;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.ShowAttributesAction;
import vg.services.main_manager.VGMainServiceHelper;
import vg.services.plugin_manager.event_and_request.event.AUIEvent;
import vg.services.plugin_manager.event_and_request.event.UIEventChangeTab;
import vg.services.plugin_manager.interfaces.IPlugin;
import vg.services.user_interface_manager.additional_swing_components.TableCellTooltipRender;
import vg.services.user_interface_manager.interfaces.AUserInterfaceElement;
import vg.services.user_interface_manager.interfaces.UserInterfaceObserverAdapter;

import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * Attribute panel plugin.
 *
 * @author Timur Zolotuhin (e-mail: tzolotuhin@gmail.com)
 */
public class AttributePlugin extends AUserInterfaceElement implements IPlugin {
    // Defines
    private final static int DEF_ROW_HEIGHT = 22;

    // Main data
    private AttributeTableModel tableModel = null;
    private JTable table;
    private Map<Attribute, MutableBoolean> vertexAttributes;
    private Map<Attribute, MutableBoolean> edgeAttributes;

    private AGraphView graphView;
    private IGraphObserver graphViewObserver;

    // Swing components
    private JButton applyButton;
    private JPanel view;
    private JCheckBox selectAll;
    private JLabel selectAllLabel;

    // Mutex
    private final Object generalMutex = new Object();

    /**
     * Constructor.
     */
    public AttributePlugin() {
        super("Attribute panel", null);
    }

    @Override
    public void install() throws Exception {
        view = new JPanel(new GridBagLayout());
        applyButton = new JButton("Apply");
        selectAll = new JCheckBox();
        selectAllLabel = new JLabel("Select all :");

        applyButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                synchronized (generalMutex) {
                    apply();
                }
            }
        });

        selectAll.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                synchronized (generalMutex) {
                    boolean check = selectAll.isSelected();
                    for(int i = 0 ; i < tableModel.getRowCount() ; i++) {
                        AttributeShower shower = (AttributeShower)tableModel.getValueAt(i, 0);
                        if(check) {
                            shower.show();
                        } else {
                            shower.unshow();
                        }
                    }
                    if(SwingUtilities.isEventDispatchThread()) {
                        table.editingStopped(null);
                        view.updateUI();
                    } else {
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                synchronized (generalMutex) {
                                    table.editingStopped(null);
                                    view.updateUI();
                                }
                            }
                        });
                    }
                }
            }
        });

        graphViewObserver = new GraphObserverAdapter() {
            @Override
            public void updateSelectedElementsAttributes(Map<Attribute, MutableBoolean> selectedVertexAttr, Map<Attribute, MutableBoolean> selectedEdgeAttr) {
                synchronized (generalMutex) {
                    vertexAttributes = selectedVertexAttr;
                    edgeAttributes = selectedEdgeAttr;
                    buildPanel();
                }
            }

            @Override
            public void changeGraphStructure() {
                synchronized (generalMutex) {
                    graphView.addAction(new GetSelectedElementAttributes() {
                        @Override
                        public void finishAction(IGraphRenderer graphRenderer) {
                            synchronized (generalMutex) {
                                vertexAttributes = getSelectedVertexAttributes();
                                edgeAttributes = getSelectedEdgeAttributes();
                                buildPanel();
                            }
                        }
                    });
                }
            }
        };

        view.setVisible(true);

        VGMainServiceHelper.userInterfaceManager.addElement(this, "attributePanel");

        VGMainServiceHelper.userInterfaceManager.addObserver(new UserInterfaceObserverAdapter() {
            @Override
            public void dispatchEvent(AUIEvent event) {
                switch (event.getType()) {
                    case DEF_CHANGE_TAB: {
                        UIEventChangeTab bufEvent = (UIEventChangeTab)event;
                        ATab tab = bufEvent.getTab();
                        if (tab != null) {
                            switch (tab.getTabType()) {
                                case ATab.DEF_GRAPH_VIEW_TAB_TYPE: {
                                    setGraphView((AGraphView)tab);
                                    break;
                                }

                                case ATab.DEF_GRAPH_COMPARISON_TAB_TYPE: {
                                    setGraphView(((AGraphComparisonView) tab).getMainGraphView());
                                    break;
                                }

                                default:
                                    break;
                            }
                        }
                        break;
                    }

                    case DEF_CHANGE_UI_STYLE: {
                        synchronized (generalMutex) {
                            buildPanel();
                            SwingUtilities.updateComponentTreeUI(view);
                        }
                        break;
                    }

                    default:
                        break;
                }
            }
        });
    }

    @Override
    public JComponent getView() {
        synchronized (generalMutex) {
            return view;
        }
    }

//==============================================================================
//-----------------PRIVATE METHODS----------------------------------------------
    /**
     * This method sets new graph view to attributes panel.
     */
    private void setGraphView(final AGraphView newGraphView) {
        if (!SwingUtilities.isEventDispatchThread()) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    setGraphView(newGraphView);
                }
            });
        } else {
            synchronized (generalMutex) {
                // Remove our observer from old graph view
                if(graphView != null) {
                    graphView.addAction(new GraphObserverAction(GraphObserverAction.REMOVE_SELECT_OBSERVER_ACTION, graphViewObserver));
                }

                // Set new graph view
                graphView = newGraphView;
                if(graphView != null) {
                    graphView.addAction(new GetSelectedElementAttributes() {
                        @Override
                        public void finishAction(IGraphRenderer graphRenderer) {
                            synchronized (generalMutex) {
                                vertexAttributes = getSelectedVertexAttributes();
                                edgeAttributes = getSelectedEdgeAttributes();
                                buildPanel();
                            }
                        }
                    });

                    graphView.addAction(new GraphObserverAction(GraphObserverAction.ADD_SELECT_OBSERVER_ACTION, graphViewObserver));
                } else {
                    vertexAttributes = Maps.newHashMap();
                    edgeAttributes = Maps.newHashMap();
                    buildPanel();
                }
            }
        }
    }

    private void apply() {
        synchronized (generalMutex) {
            HashSet<String> list_v = Sets.newHashSet();
            HashSet<String>list_e = Sets.newHashSet();
            for (int i = 0 ; i < tableModel.getRowCount() ; i++) {
                AttributeShower shower = (AttributeShower)tableModel.getValueAt(i, 0);
                if(shower.isShow() && tableModel.getValueAt(i, 3).equals("V")) {
                    list_v.add((String)tableModel.getValueAt(i, 1));
                }
                if(shower.isShow() && tableModel.getValueAt(i, 3).equals("E")) {
                    list_e.add((String)tableModel.getValueAt(i, 1));
                }
            }

            if(graphView != null) {
                graphView.addAction(new ShowAttributesAction(list_v, list_e));
            } else {
                VGMainServiceHelper.logger.printError("[" + this.getClass().getName() + ".AttributePanel.run] [BAD] IGraphView = null");
            }
        }
    }

    /**
     * This method rebuilds attributes panel.
     * Note: Call this method from EDT.
     */
    private void buildPanel() {
        this.view.removeAll();
        //---------------------------------------
        HashMap<String, AttributeRow>data = Maps.newHashMap();
        boolean checkAll = true;
        // Finding of attributes and their values
        if(this.vertexAttributes != null) {
            for(Attribute bufAttr : this.vertexAttributes.keySet()) {
                if(!bufAttr.isNullType()) {
                    AttributeRow value = data.get(bufAttr.getName());
                    MutableBoolean visible = this.vertexAttributes.get(bufAttr);
                    if(value == null) {
                        value = new AttributeRow(bufAttr.getName(), visible.booleanValue(), "V", new AttributeValue());
                        data.put(bufAttr.getName(), value);
                    }
                    switch(value.getShow()) {
                        case AttributeShower.DEF_UNSHOW_GREEN:
                        {
                            checkAll = false;
                            if(visible.booleanValue()) {
                                value.getShower().setYellowShow();
                            }
                            break;
                        }
                        case AttributeShower.DEF_SHOW_GREEN:
                        {
                            if(!visible.booleanValue()) {
                                value.getShower().setYellowShow();
                            }
                            break;
                        }
                    }
                    value.addValue(bufAttr.toStringValue());
                } else {
                    VGMainServiceHelper.logger.printError("[" + this.getClass().getName() + ".buildPanel]" + " Error in attribute panel. Attribute name or attribute value = null(Vertex)");
                }
            }
        }
        //-----------------------------------
        if(this.edgeAttributes != null) {
            for(Attribute bufAttr : this.edgeAttributes.keySet()) {
                if(!bufAttr.isNullType()) {
                    AttributeRow value = data.get(bufAttr.getName());
                    MutableBoolean visible = this.edgeAttributes.get(bufAttr);
                    if(value == null) {
                        value = new AttributeRow(bufAttr.getName(), visible.booleanValue(), "E", new AttributeValue());
                        data.put(bufAttr.getName(), value);
                    }
                    switch(value.getShow()) {
                        case AttributeShower.DEF_UNSHOW_GREEN:
                        {
                            checkAll = false;
                            if(visible.booleanValue()) {
                                value.getShower().setYellowShow();
                            }
                            break;
                        }
                        case AttributeShower.DEF_SHOW_GREEN:
                        {
                            if(!visible.booleanValue()) {
                                value.getShower().setYellowShow();
                            }
                            break;
                        }
                    }
                    value.addValue(bufAttr.toStringValue());
                } else {
                    VGMainServiceHelper.logger.printError("[" + this.getClass().getName() + ".buildPanel]" + " Error in attribute panel. Attribute name or attribute value = null(Edge)");
                }
            }
        }
        //Creating components of the filter window
        this.tableModel = new AttributeTableModel(data.values(), this.selectAll);
        //---------------------------------------
        this.table = new JTable(this.tableModel);
        this.table.setOpaque(true);
        this.table.setFillsViewportHeight(true);
        this.table.setRowHeight(DEF_ROW_HEIGHT);
        TableRowSorter<AttributeTableModel> sorter = new TableRowSorter<>(this.tableModel);
        sorter.setSortable(0, true);
        sorter.setSortable(1, true);
        sorter.setSortable(2, false);
        sorter.setSortable(3, true);
        this.table.setRowSorter(sorter);
        this.table.setShowGrid(true);
        this.table.setDefaultRenderer(String.class, new TableCellTooltipRender());
        this.table.getTableHeader().setReorderingAllowed(false);
        //---------------------------------------
        AttributeTableCellRenderer renderer = new AttributeTableCellRenderer();
        TableColumn col0 = this.table.getColumnModel().getColumn(0);
        TableColumn col2 = this.table.getColumnModel().getColumn(2);
        col0.setCellRenderer(renderer);
        col2.setCellRenderer(renderer);
        //---------------------------------------
        AttributeTableCellEditor editor = new AttributeTableCellEditor(this.tableModel, renderer);
        col0.setCellEditor(editor);
        col2.setCellEditor(editor);
        // Package of interface
        if(this.graphView != null) {
            JScrollPane scroll = new JScrollPane(this.table);
            view.add(scroll, new GridBagConstraints(0,0,  3,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0));

            view.add(this.selectAllLabel, new GridBagConstraints(0,1,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0));

            view.add(this.selectAll, new GridBagConstraints(1,1,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0));
            selectAll.setSelected(checkAll);

            view.add(this.applyButton, new GridBagConstraints(2,1,  1,1,  1,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 50, 5, 50),  0,0));
        }
        view.updateUI();
    }
}
